package com.hst.web.auth.web;

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.*;
import javax.validation.Valid;

import cn.hutool.core.io.IoUtil;
import com.github.pagehelper.util.StringUtil;
import com.google.code.kaptcha.Producer;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.hst.web.auth.util.CommonConstants;
import com.hst.web.auth.util.LoginUtil;
import lombok.SneakyThrows;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.*;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.hst.core.ServiceContext;
import com.hst.core.ServiceException;
import com.hst.core.annotation.WebAuth;
import com.hst.core.dao.IORDao;
import com.hst.core.dao.ISQLDao;
import com.hst.core.dao.ORQuery;
import com.hst.core.dao.ORQuery.Op;
import com.hst.core.json.JsonUtil;
import com.hst.web.auth.po.ChgPwdRequest;
import com.hst.web.auth.po.LoginRequest;
import com.hst.web.auth.po.User;
import com.hst.web.auth.service.UserAuthService;
import com.hst.web.base.entity.TAccountInfo;
import com.hst.web.base.entity.TCompanyEmployee;
import com.hst.web.base.entity.TDeptEmployee;
import com.hst.web.base.entity.TEmployeeContractorDept;
import com.hst.web.base.ui.CompanyEmployee;
import com.hst.web.common.utils.HttpRequestUtils;
import com.hst.web.common.utils.PapersNumberUtils;
import com.hst.web.sys.entity.TUser;
import com.hst.web.sys.entity.TUserSession;
import com.hst.web.sys.service.UserService;

/**
 * @author luwenjie
 *
 */
@Controller
@RequestMapping("/session")
public class SessionController {

	@Autowired
	UserAuthService auth;
	
	@Autowired
	UserService userService;

	@Autowired
	IORDao dao;

	@Autowired
	ISQLDao sql;

	@Resource
	private Producer producer;

	/**
	 * 获取用户信息
	 *
	 * @param m
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@RequestMapping(method = RequestMethod.GET)
	public void get(String menu, String auths, HttpServletRequest request, Model m) {
		// sessions
		User u = (User) ServiceContext.getInstance().getUser();
		if (null != u) {
			m.addAttribute(ServiceContext.USER, u);
			Map<String,Object> map = new HashMap<String,Object>();
			map.put("userid", u.getId());

			List<Map<String,String>> rolesMap = (List<Map<String,String>>) sql.query("session.roleLevels",map);
			if (rolesMap.size() == 0) {
				return;
			}
			Map<String, String> roleMap = rolesMap.get(0);

			String level = roleMap.get("ROLE_LEVEL");
			String id = roleMap.get("ID");

			m.addAttribute("role",level);
			m.addAttribute("roleid",id);

			if(!"9".equals(level)) {
				TUser user = dao.get(TUser.class, u.getId());
				if(StringUtils.isNotEmpty(user.getEmployeeCode())) {
					TCompanyEmployee emp = dao.get(TCompanyEmployee.class, user.getEmployeeCode());
					if(StringUtils.isEmpty(emp.getPapersNumber()) || StringUtils.isEmpty(emp.getPapersType())) {
						m.addAttribute("msg","error");
						return;
					}
				}else {
					m.addAttribute("msg","error");
					return;
				}
			}

			if("2".equals(level)) {
				if(StringUtils.isNotEmpty(u.getCompanyCode())) {
					m.addAttribute("companyCode",u.getCompanyCode());
					m.addAttribute("companyName",u.getCompanyName());
				}
				List<Map> maps = (List<Map>) sql.query("user.listCompanys",map);
				if(maps.size() > 0 && StringUtils.isEmpty(u.getCompanyCode())) {
					Map<String,Object> map1 = maps.get(0);
					if(map1 != null) {
						if(map1.containsKey("COMPANY_CODE")) {
							m.addAttribute("companyCode",map1.get("COMPANY_CODE"));
							u.setCompanyCode((String) map1.get("COMPANY_CODE"));
						}
						if(map1.containsKey("COMPANY_NAME")) {
							m.addAttribute("companyName",map1.get("COMPANY_NAME"));
							u.setCompanyName((String) map1.get("COMPANY_NAME"));
						}
						ServiceContext.getInstance().setUser(u);
					}
				}
				m.addAttribute("companys",maps);
			}

			if("1".equals(level)) {
				if(StringUtils.isNotEmpty(u.getCompanyCode())) {
					m.addAttribute("companyCode",u.getCompanyCode());
					m.addAttribute("companyName",u.getCompanyName());
				}
				List<Map> maps = (List<Map>) sql.query("user.listContractors",map);
				if(maps.size() > 0 && StringUtils.isEmpty(u.getCompanyCode())) {
					Map<String,Object> map1 = maps.get(0);
					if(map1 != null) {
						if(map1.containsKey("COMPANY_CODE")) {
							m.addAttribute("companyCode",map1.get("COMPANY_CODE"));
							u.setCompanyCode((String) map1.get("COMPANY_CODE"));
						}
						if(map1.containsKey("COMPANY_NAME")) {
							m.addAttribute("companyName",map1.get("COMPANY_NAME"));
							u.setCompanyName((String) map1.get("COMPANY_NAME"));
						}
						ServiceContext.getInstance().setUser(u);
					}
				}
				m.addAttribute("companys",maps);
			}
		}

		// cookies
		Cookie[] cookies = request.getCookies();
		if (null != cookies) {
			for (Cookie c : cookies) {
				if (ServiceContext.USERID.equals(c.getName())) {
					m.addAttribute(ServiceContext.USERID, c.getValue());
					break;
				}
			}
		}

		// menu
		if ("1".equals(menu) && (null != u)) {
			menu(m);
		}

		// menu
		if ("1".equals(auths) && (null != u)) {
			this.auths(m);
		}
	}

	@WebAuth
	@RequestMapping("/menu")
	public void menu(Model m) {
		User u = (User) ServiceContext.getInstance().getUser();
		Map<String, String> params = new HashMap<String, String>();
		params.put("userid", u.getId());
		m.addAttribute("menu", sql.query("session.menus", params));
	}

	@WebAuth
	@RequestMapping("/auths")
	public void auths(Model m) {
		User u = (User) ServiceContext.getInstance().getUser();
		m.addAttribute("auths", u.getPrivileges());
	}

	@GetMapping(path = "/code/{randomStr}")
	public void valicode(@PathVariable String randomStr, HttpServletResponse response) throws IOException {
		Assert.isTrue(StringUtil.isNotEmpty(randomStr), "机器码不能为空");
		response.setHeader("Cache-Control", "no-store, no-cache");
		response.setHeader("Transfer-Encoding", "JPG");
		response.setContentType("image/jpeg");
		//生成文字验证码
		String text = producer.createText();
		//生成图片验证码
		BufferedImage image = producer.createImage(text);
		LoginUtil.CAPTCHA_CACHE.put(randomStr, text);
		//创建输出流
		ServletOutputStream out = response.getOutputStream();
		//写入数据
		ImageIO.write(image, "jpeg", out);
		IoUtil.close(out);
	}

	@RequestMapping(method = { RequestMethod.PUT, RequestMethod.POST })
	public Object login(@RequestBody String loginRequest, HttpServletRequest request, HttpServletResponse response,
			Model m) throws Exception {
		LoginRequest login = JsonUtil.read(loginRequest, LoginRequest.class);
		// 验证码校验
		try{
			LoginUtil.checkCode(login);
		}catch (Exception e) {
			m.addAttribute("message", e.getMessage());
			return m;
		}
		Encoder encoder = Base64.getEncoder();
		String psw = new String(encoder.encode(login.getPasswd().getBytes("UTF-8")));
		User u = (User) auth.getUser(login.getUserid(), psw);
		if (u != null) {
			// update to db
			String sid = UUID.randomUUID().toString();
			TUserSession us = new TUserSession(sid, u.getId(), TUserSession.STATUS_ON, new Timestamp(new Date().getTime()));
			us.setIp(HttpRequestUtils.getClientIp(request));
			us.setAgent(request.getHeader("User-Agent"));
			us.setClntUser(request.getRemoteUser());
			dao.save(us);

			// update to session
			u.setSid(sid);
			ServiceContext.getInstance().setUser(u);
			ServiceContext.getInstance().toSession(request, response);
			m.addAttribute("user", u);

			// update to cookie
			String uid = DigestUtils.md5Hex(u.getUserid());
			Cookie cookie = new Cookie(ServiceContext.USERID, uid);
			cookie.setPath("/");
			cookie.setHttpOnly(true);
			cookie.setMaxAge(login.getRemember() ? Integer.MAX_VALUE : -1);
			response.addCookie(cookie);
			cookie = new Cookie(ServiceContext.SID, u.getSid());
			cookie.setPath("/");
			cookie.setHttpOnly(true);
			cookie.setMaxAge(login.getRemember() ? Integer.MAX_VALUE : -1);
			response.addCookie(cookie);
		}
		return m;
	}

	@WebAuth
	@RequestMapping(method = { RequestMethod.DELETE })
	public void logout(HttpServletRequest request, HttpServletResponse response, Model m) {
		User u = (User) ServiceContext.getInstance().getUser();
		if (u != null) {
			ServiceContext.getInstance().setUser(null);

			// update to cookie;
			Cookie cookie = new Cookie(ServiceContext.USERID, u.getUserid());
			cookie.setPath("/");
			cookie.setMaxAge(0);
			response.addCookie(cookie);
			cookie = new Cookie(ServiceContext.SID, u.getSid());
			cookie.setPath("/");
			cookie.setMaxAge(0);
			response.addCookie(cookie);

			// update db
			TUserSession us = u.getSid() == null ? null : dao.get(TUserSession.class, u.getSid());
			if (null != us) {
				us.setStatus(TUserSession.STATUS_OFF);
				dao.update(us);
			}
		}
		m.addAttribute("user", u);

		// update to session
		request.getSession().invalidate();
	}

	@WebAuth
	@RequestMapping(value = "/chgPwd", method = RequestMethod.POST)
	public void chgPwd(@RequestBody String data, Model m)
			throws JsonParseException, JsonMappingException, IOException, ServiceException {
		ChgPwdRequest request = JsonUtil.read(data, ChgPwdRequest.class);
		Encoder encoder = Base64.getEncoder();
		String psw1 = new String(encoder.encode(request.getPwd1().getBytes("UTF-8")));
		String psw2 = new String(encoder.encode(request.getPwd2().getBytes("UTF-8")));
		User u = (User) ServiceContext.getInstance().getUser();
		TUser user = dao.get(TUser.class, u.getId());
		if (!user.getPassword().equals(psw1)) {
			throw new ServiceException("旧密码输入错误!");
		}
//		if (!checkPwd(request.getPwd2())) {
//			throw new ServiceException("新密码需为-长度8-20位，必须包含数字,字母,特殊符号");
//		}
		user.setPassword(psw2);
		dao.update(user);
		
		String employeeCode = user.getEmployeeCode();
		//同步更新TAccountInfo表的password字段
		String userName = userService.vaildateAccountIdentity(employeeCode);
		if (StringUtils.isNotEmpty(userName)) {
			TAccountInfo accountInfo = dao.get(TAccountInfo.class, userName);
			if (accountInfo != null) {
				accountInfo.setPassword(psw2);
				dao.update(accountInfo);
			}
		}		
	}

	/**
	 * 新密码验证
	 * @param pwd2
	 * @return
	 */
	private static boolean checkPwd(String pwd2){
		String reg = "^(?=(.*[a-zA-Z]){1,})(?=(.*[\\d]){1,})(?=(.*(\\W|[_])){1,})(?!.*[\\s\\u4e00-\\u9fa5]).{8,20}$";
		if (pwd2.matches(reg)) {
			return true;
		} else {
			return false;
		}
	}

	@SuppressWarnings({ "unchecked" })
	private Map<String, Object> parseQuerys(String data) throws JsonParseException, JsonMappingException, IOException {
		Map<String, Object> querys = JsonUtil.read(data, Map.class);
		return querys;
	}

	@WebAuth
	@RequestMapping(value = "/changeCompanyCode", method = RequestMethod.POST)
	public void changeCompanyCode(@RequestBody String data, Model m) throws JsonParseException, JsonMappingException, IOException {
		Map<String, Object> params = parseQuerys(data);
		String companyCode = (String) params.get("companyCode");
		String companyName = (String) params.get("companyName");
		User u = (User) ServiceContext.getInstance().getUser();
		u.setCompanyCode(companyCode);
		u.setCompanyName(companyName);
		ServiceContext.getInstance().setUser(u);
	}

	@WebAuth
	@RequestMapping(value = "/changeContractorCode", method = RequestMethod.POST)
	public void changeContractorCode(@RequestBody String data, Model m) throws JsonParseException, JsonMappingException, IOException {
		Map<String, Object> params = parseQuerys(data);
		String contractorCode = (String) params.get("contractorCode");
		String contractorName = (String) params.get("contractorName");
		String companyCode = (String) params.get("companyCode");
		String companyName = (String) params.get("companyName");
		User u = (User) ServiceContext.getInstance().getUser();
		u.setContractorCode(contractorCode);
		u.setContractorName(contractorName);
		u.setCompanyCode(companyCode);
		u.setCompanyName(companyName);
		ServiceContext.getInstance().setUser(u);
	}

	@WebAuth
	@RequestMapping(value = "/completeEmployee", method = RequestMethod.POST)
	public void completeEmployee(@RequestBody String data, Model m) throws Exception {
		CompanyEmployee employee = JsonUtil.read(data, CompanyEmployee.class);
		User u = (User) ServiceContext.getInstance().getUser();
		TUser user = dao.get(TUser.class, u.getId());
		if(StringUtils.isNotEmpty(user.getEmployeeCode())) {
			TCompanyEmployee e = dao.get(TCompanyEmployee.class, user.getEmployeeCode());
			if(e != null) {
				try {
					PapersNumberUtils.validate(employee.getPapersNumber(), employee.getPapersType());
				} catch (Exception e3) {
					m.addAttribute("msg",e3.getMessage());
					return;
				}
				List<ORQuery> querys = new ArrayList<ORQuery>();
				querys.add(new ORQuery(Op.eq,"papersNumber",employee.getPapersNumber()));
				querys.add(new ORQuery(Op.eq,"papersType",employee.getPapersType()));
				List<TCompanyEmployee> emps = dao.list(TCompanyEmployee.class,querys);
				if(emps.size() > 0) { //验证有重复
					String employeeCode = emps.get(0).getEmployeeCode();
					// 验证是否有其他账号使用
					querys = new ArrayList<ORQuery>();
					querys.add(new ORQuery(Op.eq,"employeeCode",employeeCode));
					List<TUser> users = dao.list(TUser.class,querys);
					if(users.size() > 0) { //已经有其他账号绑定
						m.addAttribute("msg","已存在重复的证件类型+证件号");
						return;
					}else {
						List<TDeptEmployee> deptEmployees = dao.list(TDeptEmployee.class,querys);
						List<TEmployeeContractorDept> contEmployees = dao.list(TEmployeeContractorDept.class,querys);
						if(contEmployees.size() > 0) { // 有承包商员工使用了此身份证
							m.addAttribute("msg","已存在重复的证件类型+证件号");
							return;
						}
						if(deptEmployees.size() > 0) { // 有企业员工使用了此身份证
							//判断是否为同一家公司
							String companyCode = deptEmployees.get(0).getCompanyCode();
							querys = new ArrayList<ORQuery>();
							querys.add(new ORQuery(Op.eq,"employeeCode",user.getEmployeeCode()));
							querys.add(new ORQuery(Op.eq,"companyCode",companyCode));
							List<TDeptEmployee> emps1 = dao.list(TDeptEmployee.class,querys);
							if(emps1.size() > 0) { //是同一家企业 修改账号绑定的员工
								// 名字是否相同
								TCompanyEmployee obj = dao.get(TCompanyEmployee.class, employeeCode);
								if(employee.getFullname().equals(obj.getFullname())) { // 名字相同
									//删除原来虚构的员工
									TCompanyEmployee obj1 = dao.get(TCompanyEmployee.class, user.getEmployeeCode());
									dao.delete(obj1);

									user.setEmployeeCode(employeeCode);
									dao.update(user);
									return;
								}else {
									m.addAttribute("msg","已存在重复的证件类型+证件号");
									return;
								}
							} else {
								m.addAttribute("msg","已存在重复的证件类型+证件号");
								return;
							}
						} else { //只是基本库中有此人的数据
							// 修改账号绑定的员工 建立企业和员工的关联关系
							querys = new ArrayList<ORQuery>(); // 查询原本的关联关系
							querys.add(new ORQuery(Op.eq,"employeeCode",user.getEmployeeCode()));
							List<TDeptEmployee> depts = dao.list(TDeptEmployee.class,querys);
							if(depts.size() > 0) {
								String companyCode = depts.get(0).getCompanyCode();
								TDeptEmployee deptRel = new TDeptEmployee();
								deptRel.setCompanyCode(companyCode);
								deptRel.setEmployeeCode(employeeCode);
								dao.save(deptRel);

								//删除原来的关联关系
								dao.delete(depts.get(0));
							}
							//删除原本新建的人
							TCompanyEmployee obj = dao.get(TCompanyEmployee.class,user.getEmployeeCode());
							dao.delete(obj);

							user.setEmployeeCode(employeeCode);
							dao.update(user);
						}
					}
				} else { //验证没有重复
					e.setFullname(employee.getFullname());
					e.setPapersNumber(employee.getPapersNumber());
					e.setPapersType(employee.getPapersType());
					dao.update(e);
				}
				/*try {
					PapersNumberUtils.duplicate(employee.getPapersNumber(), employee.getPapersType(), e.getEmployeeCode());
				} catch (Exception e2) {
					m.addAttribute("msg",e2.getMessage());
					return;
				}*/
			}
		}else {
			TCompanyEmployee e = new TCompanyEmployee();
			try {
				PapersNumberUtils.duplicate(employee.getPapersNumber(), employee.getPapersType(), null);
			} catch (Exception e2) {
				m.addAttribute("msg",e2.getMessage());
				return;
			}
			try {
				PapersNumberUtils.validate(employee.getPapersNumber(), employee.getPapersType());
			} catch (Exception e3) {
				m.addAttribute("msg",e3.getMessage());
				return;
			}
			e.setFullname(employee.getFullname());
			e.setPapersNumber(employee.getPapersNumber());
			e.setPapersType(employee.getPapersType());
			e = dao.save(e);
			user.setEmployeeCode(e.getEmployeeCode());
			dao.update(user);
		}
	}
}
